package com.mxx.common.security;

import com.mxx.common.exception.EncryptionException;
import com.mxx.common.exception.IOException;
import com.mxx.common.utils.SignUtil;
import com.mxx.common.utils.Strings;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;
import java.security.UnrecoverableEntryException;
import java.util.Base64;
import java.util.Optional;

/**
 * @Author itzgyw
 * @Date 2020-02-11
 * @Time 18:53
 * @Description:
 */
public class DESUtil {
    private final static String DES = "DES";


    public static void main(String[] args) {
        Optional<String> key = getKey();
        Optional<String> encrypt = DESUtil.encrypt("111", key.get());
        Optional<String> decrypt = DESUtil.decrypt(encrypt.get(), key.get());
        System.out.println(encrypt.get());
        System.out.println(decrypt.get());
    }

    public static Optional<String> getKey() {

        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(DES);
            keyGenerator.init(56);
            // 生成一个Key
            SecretKey generateKey = keyGenerator.generateKey();
            // 转变为字节数组
            byte[] encoded = generateKey.getEncoded();
            // 生成密钥字符串
            String key = Base64.getEncoder().encodeToString(encoded);
            return Optional.ofNullable(key);
        } catch (Exception e) {
            e.printStackTrace();
            return Optional.empty();
        }
    }


    /**
     * Description 根据键值进行加密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    public static Optional<String> encrypt(String data, String key) {
        Optional<byte[]> optionalBytes = encrypt(Strings.toByte(data), Strings.toByte(key));
        if (optionalBytes.isPresent()) {
            return Optional.of(SignUtil.byteToHex(optionalBytes.get()));
        } else {
            return Optional.empty();
        }

    }

    /**
     * Description 根据键值进行加密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    public static Optional<String> encryptBase64(String data, String key) {
        Optional<byte[]> optionalBytes = encrypt(Strings.toByte(data), Base64.getDecoder().decode(key));
        if (optionalBytes.isPresent()) {
            return Optional.of(Base64.getEncoder().encodeToString(optionalBytes.get()));
        } else {
            return Optional.empty();
        }
    }

    /**
     * Description 根据键值进行解密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static Optional<String> decrypt(String data, String key) {
        if (data == null)
            return null;

        byte[] buf = SignUtil.hexToByte(data);
        Optional<byte[]> optionalBytes = decrypt(buf, Strings.toByte(key));
        if (optionalBytes.isPresent()) {
            return Optional.of(Strings.toString(optionalBytes.get()));
        } else {
            return Optional.empty();
        }

    }

    /**
     * Description 根据键值进行解密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static Optional<String> decryptBase64(String data, String key) {
        if (data == null || data.isEmpty())
            return Optional.empty();

        byte[] buf = Base64.getDecoder().decode(data);
        Optional<byte[]> optionalBytes = decrypt(buf, Base64.getDecoder().decode(key));
        if (optionalBytes.isPresent()) {
            return Optional.of(Strings.toString(optionalBytes.get()));
        } else {
            return Optional.empty();
        }

    }

    /**
     * Description 根据键值进行加密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    private static Optional<byte[]> encrypt(byte[] data, byte[] key) {
        try {
            // 生成一个可信任的随机数源
            SecureRandom sr = new SecureRandom();

            // 从原始密钥数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);

            // 创建一个密钥工厂，然后用它把DESKeySpec转换成SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);

            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance(DES);

            // 用密钥初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);

            return Optional.ofNullable(cipher.doFinal(data));
        } catch (Exception e) {
            throw new EncryptionException("DSC encrypt exception ", e);
        }
    }

    /**
     * Description 根据键值进行解密
     *
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    private static Optional<byte[]> decrypt(byte[] data, byte[] key) {
        try {


            // 生成一个可信任的随机数源
            SecureRandom sr = new SecureRandom();

            // 从原始密钥数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);

            // 创建一个密钥工厂，然后用它把DESKeySpec转换成SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);

            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance(DES);

            // 用密钥初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, securekey, sr);

            return Optional.of(cipher.doFinal(data));
        } catch (Exception e) {
            throw new EncryptionException("DSC decrypt exception ", e);
        }
    }

}
